package com.zimmur.tenpay.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zimmur.tools.HttpClientUtil;

public class WechatUtil {
	private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
	public static final String TAG = "Wechat.Util";
	private static final int timeout = 5000;
	// 初始化
	public static String APP_ID = "wx3c575a5506b11cad";// 微信开发平台应用id

	// 应用对应的密钥
	public static String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 获取预支付id的接口url
	public static String PARTNER = "1493559792";// 商户号
	public static String API_KEY = "Qingyun25a0e56e42f9662180dc12049";// 商户API安全KEY

	public static String NOTIFY_URL = "http://service.popolook.com/restful/pay/wxpay/notify";// 商户号对应的密钥

	// 00=待付款/10=已付款待发货/20=已发货待完成/30=已完成/90=订单取消
	private static final List<String> arrOrderStatus = new ArrayList(Arrays.asList("10", "20", "30", "40", "50", "90"));

	public static void setWxParameter(String appId,String wxKey,String gateUrl,String partnerId,String notifyUrl){
		APP_ID = appId;
		API_KEY = wxKey;
		PARTNER = partnerId;
		GATEURL = gateUrl;
		NOTIFY_URL = notifyUrl;
	}
	
	//订单状态合法性验证
	public static boolean validOrderStats(String orderStatus){
		return arrOrderStatus.contains(orderStatus);
	}
	

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (null != value && !"".equals(value) && !"sign".equals(key) && !"key".equals(key)) {
				if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
					prestr = prestr + key + "=" + value;
				} else {
					prestr = prestr + key + "=" + value + "&";
				}
			}
		}

		return prestr;
	}
	
	/**
	 * 根据反馈回来的信息，生成签名结果
	 * 
	 * @param Params
	 *            通知返回来的参数数组
	 * @param sign
	 *            比对的签名结果
	 * @return 生成的签名结果
	 */
	public static boolean getSignVeryfy(Map<String, String> Params, String sign) {
		// 过滤空值、sign与sign_type参数
		// Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
		// 获取待签名字符串
		String preSignStr = createLinkString(Params);
		logger.info("createLinkString:"+preSignStr);
		preSignStr += "&key=" + API_KEY;
		// 获得签名验证结果
		String resultSign = MD5.MD5Encode(preSignStr).toUpperCase();
		// String resultSign = MD5Util.MD5Encode(preSignStr.toString(), "UTF-8").toLowerCase();
		if (sign.equals(resultSign)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 装配xml,生成请求prePayId所需参数
	 * 
	 * @param params
	 * @return
	 */
	public static String toXml(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		sb.append("<xml>");
		for (int i = 0; i < params.size(); i++) {
			sb.append("<" + params.get(i).getName() + ">");			
			sb.append("<![CDATA["+params.get(i).getValue()+"]]>");
			sb.append("</" + params.get(i).getName() + ">");
		}
		sb.append("</xml>");
		return sb.toString(); 
	}

	/**
	 * 生成签名
	 */
	public static String genPackageSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < params.size(); i++) {
			String k = params.get(i).getName();
			String v = params.get(i).getValue();
			if(null != v && !"".equals(v) 
					&& !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=");
		sb.append(API_KEY);
		String packageSign = MD5.MD5Encode(sb.toString()).toUpperCase();
		return packageSign;
	}
	/**
	 * 生成签名
	 */
	public static String genPackageSignNew(List<NameValuePair> params) {
		Map<String, Object> map = new HashMap<>();
		for (int i = 0; i < params.size(); i++) {
			String k = params.get(i).getName();
			String v = params.get(i).getValue();
			
			if(null != v && !"".equals(v) 
					&& !"sign".equals(k) && !"key".equals(k)) {
				map.put(k, v);
			}
		}		
		String packageSign = getSign(map);
		return packageSign;
	}
	/**
	 * 生成预支付数据
	 * @param goodOrderNo 商户订单号
	 * @param body 商品描述
	 * @param noticeUrl 通知地址
	 * @param ip 终端IP
	 * @param totalFee 总金额
	 * @return
	 */
	public static String genProductArgs(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) {
		StringBuffer xml = new StringBuffer();
		try {
			String nonceStr = getNonceStr();
			xml.append("</xml>");
			List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
			packageParams.add(new BasicNameValuePair("appid", APP_ID));
			packageParams.add(new BasicNameValuePair("body", body));
			packageParams.add(new BasicNameValuePair("mch_id", PARTNER));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", noticeUrl));
			packageParams.add(new BasicNameValuePair("out_trade_no", goodOrderNo));
			packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
			packageParams.add(new BasicNameValuePair("total_fee", totalFee));
			packageParams.add(new BasicNameValuePair("trade_type", "APP")); //交易类型
			String sign = genPackageSign(packageParams);
			packageParams.add(new BasicNameValuePair("sign", sign));
			String xmlstring = toXml(packageParams);
			return xmlstring;
		} catch (Exception e) {
			logger.info("genProductArgs fail, ex = " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * 生成预支付数据
	 * @param goodOrderNo 商户订单号
	 * @param body 商品描述
	 * @param noticeUrl 通知地址
	 * @param ip 终端IP
	 * @param totalFee 总金额
	 * @return
	 */
	public static String genProductArgsNew(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) {
		try {
			String nonceStr = getNonceStr();
			List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
			packageParams.add(new BasicNameValuePair("appid", APP_ID));
			packageParams.add(new BasicNameValuePair("body", body));
			packageParams.add(new BasicNameValuePair("mch_id", PARTNER));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", noticeUrl));
			packageParams.add(new BasicNameValuePair("out_trade_no", goodOrderNo));
			packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
			packageParams.add(new BasicNameValuePair("total_fee", totalFee));
			packageParams.add(new BasicNameValuePair("trade_type", "APP")); //交易类型
			String sign = genPackageSignNew(packageParams);
			packageParams.add(new BasicNameValuePair("sign", sign));
			String xmlstring = toXml(packageParams);
			// 这一步最关键 我们把字符转为 字节后,再使用“ISO8859-1”进行编码，得到“ISO8859-1”的字符串  
			return xmlstring;
		} catch (Exception e) {
			logger.info("genProductArgs fail, ex = " + e.getMessage());
			return null;
		}
	}
	/**
	 * 生成app支付签名
	 * 
	 * @param params
	 * @return
	 */
	public static String genAppSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		sb.append("key=");
		sb.append(API_KEY);
		String appSign = MD5Util.MD5Encode(sb.toString(),"UTF-8");
		
		return appSign;
	}
	/**
     * 签名算法
     * @param o 要参与签名的数据对象
     * @return 签名
     * @throws IllegalAccessException
     */
    public static String getSign(Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + API_KEY;
        //Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }
	/**
	 * 生成调用微信app支付所需参数
	 * 
	 * @param prepayId
	 * @return
	 */
	public static Map<String, String> genPayReq(String prepayId) {
		Map<String, String> resultMap = new HashMap<String, String>();
		String timeStamp = getTimeStamp();
		String nonceStr = getNonceStr();
		Map<String, Object> signParams = new HashMap<String, Object>();
		signParams.put("appid", APP_ID);
		signParams.put("partnerid", PARTNER);
		signParams.put("prepayid", prepayId);
		signParams.put("package", "Sign=WXPay");
		signParams.put("noncestr", nonceStr);		
		signParams.put("timestamp", timeStamp);
		
		String sign = getSign(signParams);
		resultMap.put("appid", APP_ID);
		resultMap.put("noncestr", nonceStr);
		resultMap.put("package", "Sign=WXPay");
		resultMap.put("partnerid", PARTNER);
		resultMap.put("prepayid", prepayId);
		resultMap.put("timestamp", timeStamp);
		resultMap.put("sign", sign);
		return resultMap;
	}
	/**
	 * 生成调用微信app支付所需参数
	 * 
	 * @param prepayId
	 * @return
	 */
	public static Map<String, String> testGenPayReq(String prepayId) {
		Map<String, String> resultMap = new HashMap<String, String>();
		String timeStamp = "1465285044";
		String nonceStr = "0E1BACF07B14673FCDB553DA51B999A5";
		Map<String, Object> signParams = new HashMap<String, Object>();		
		signParams.put("appid", "wxa732adebdcad23f5");
		signParams.put("partnerid", "1309935401");
		signParams.put("prepayid", prepayId);
		signParams.put("package", "Sign=WXPay");
		signParams.put("noncestr", nonceStr);		
		signParams.put("timestamp", timeStamp);
		
		String sign = getSign(signParams);
		resultMap.put("appid", APP_ID);
		resultMap.put("noncestr", nonceStr);
		resultMap.put("package", "Sign=WXPay");
		resultMap.put("partnerid", PARTNER);
		resultMap.put("prepayid", prepayId);
		resultMap.put("timestamp", timeStamp);
		resultMap.put("sign", sign);
		return resultMap;
	}
	/**
	 * 微信支付生成预支付订单
	 * 
	 * @throws IOException
	 * @throws JDOMException
	 */
	public static Map<String, String> getPayPreId(String goodOrderNo, String body, String productId,
			String noticeUrl, String ip, String totalFee,String tradeType,String sceneInfo) throws Exception {
		String paramsXml = generateXMLContent(body, goodOrderNo, productId, ip, totalFee, tradeType,sceneInfo);
		//logger.info("key:"+API_KEY+" paramsXml:"+paramsXml);
		/*byte[] buf = WechatUtil.httpPost(GATEURL, paramsXml);
		String contentXml = new String(buf,"utf-8");*/
		String contentXml = HttpClientUtil.doPostXml(GATEURL, paramsXml);
		Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml);
		return resultMap;
	}
	/**
	* <p>Title: generateXMLContent</p>
	* <p>Description: PC扫码支付生成</p>
	* @param appid   公众账号ID
	* @param body
	* @param mchId   微信支付分配的商户号
	* @param nonceStr  随机字符串(32位以内)
	* @param notifyUrl  通知地址
	* @param outTradeNo 商户订单号
	* @param productId 商品ID 
	* @param spbillCreateIp  APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
	* @param totalFee   金额，单位为分
	* @param tradeType   JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付、MWEB--H5支付
	* @param sign 签名
	* @return
	 */
	 public static String generateXMLContent(String body, String outTradeNo,String productId,String spbillCreateIp
			 ,String totalFee,String tradeType,String sceneInfo) {  
		    String nonceStr = getNonceStr();
		    SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
		    if(tradeType.equals("MWEB")){
		    	packageParams.put("scene_info", sceneInfo);  
		    }
		    packageParams.put("appid", APP_ID);  
	        packageParams.put("mch_id", PARTNER);  
	        packageParams.put("nonce_str", nonceStr);  
	        packageParams.put("body", body);  //（调整为自己的名称）
	        packageParams.put("out_trade_no", outTradeNo);  
	        packageParams.put("total_fee", totalFee); //价格的单位为分  
	        packageParams.put("spbill_create_ip", spbillCreateIp);  
	        packageParams.put("notify_url", NOTIFY_URL);  
	        packageParams.put("trade_type", tradeType); 
	        String sign = createSign("UTF-8",packageParams,API_KEY);
	        packageParams.put("sign", sign);
	        String requestXML = getRequestXml(packageParams);
	        return requestXML;  
	 }
	 /**
	 * <p>Title: doUnifiedOrder</p>
	 * <p>Description:统一下单接口（扫码支付） </p>
	 * @param subject
	 * @param totalFee
	 * @param spbillCreateIp
	 * @param tradetype
	 * @param productId
	 * @param outTradeNo
	 * @return 返回二维码图片二进制字符串
	  */
	 public static Map<String,String> doUnifiedOrder(String subject,String totalFee,String spbillCreateIp,
			 String tradetype,String productId,String outTradeNo,String sceneInfo){
			//1.生成调用统一下单接口的入参
			String Imagestr = "";
			Map<String, String> resultMap = new HashMap<String, String>();
			String xmlParam = generateXMLContent(subject, outTradeNo, productId, spbillCreateIp, totalFee, tradetype,sceneInfo);
			HttpClient httpClient = HttpClientBuilder.create().build();   
	        HttpPost post = new HttpPost(WXPayConstants.UNIFIEDORDER_URL);  
	        post.addHeader("Content-Type", "text/xml; charset=UTF-8");  
	        StringEntity xmlEntity = new StringEntity(xmlParam, ContentType.TEXT_XML);  
	        post.setEntity(xmlEntity);
	        try {
	        	HttpResponse httpResponse = httpClient.execute(post);
	        	//2.解析code_url
	        	String responseXML = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
	             resultMap = (Map<String, String>)XMLUtil.doXMLParse(responseXML);  
	             post.releaseConnection();  
			} catch (Exception e) {
				e.printStackTrace();
			}
			return resultMap;
		}
	 /**
	 * <p>Title: getRequestXml(扫码支付)</p>
	 * <p>Description: 生成请求的入参</p>
	 * @param parameters
	 * @return
	  */
	 public static String getRequestXml(SortedMap<Object, Object> parameters) {  
	        StringBuffer sb = new StringBuffer();  
	        sb.append("<xml>");  
	        Set es = parameters.entrySet();  
	        Iterator it = es.iterator();  
	        while (it.hasNext()) {  
	            Map.Entry entry = (Map.Entry) it.next();  
	            String k = (String) entry.getKey();  
	            String v = (String) entry.getValue();  
	            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {  
	                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");  
	            } else {  
	                sb.append("<" + k + ">" + v + "</" + k + ">");  
	            }  
	        }  
	        sb.append("</xml>");  
	        return sb.toString();  
	    }  
     /**
     * <p>Title: createSign</p>
     * <p>Description: 生成签名（扫码支付）</p>
     * @param characterEncoding
     * @param packageParams
     * @param API_KEY
     * @return
      */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {  
        StringBuffer sb = new StringBuffer();  
        Set es = packageParams.entrySet();  
        Iterator it = es.iterator();  
        while (it.hasNext()) {  
            Map.Entry entry = (Map.Entry) it.next();  
            String k = (String) entry.getKey();  
            String v = (String) entry.getValue();  
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" + API_KEY);  
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
        return sign;  
    } 
    
	public static String getNonceStr() {
		Random random = new Random();
		return Sha1Util.createSign(String.valueOf(random.nextInt(10000)));
	}

	public static String getTimeStamp() {
		return String.valueOf(System.currentTimeMillis() / 1000);
	}
	
}
